package cn.caplike.demo.nio._2021.selector;

import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * Description: Selector.
 * <br>Details: 由一个线程监听5个端口的事件, 处理多个客户端的连接 (依靠 Selector)
 *
 * @author LiKe
 * @version 1.0.0
 * @date 2022-01-21 12:44
 */
public class SelectorDemo {

    public static void main(String[] args) throws Exception {
        final int[] ports = new int[5];

        ports[0] = 5000; // telnet localhost 5000
        ports[1] = 5001;
        ports[2] = 5002;
        ports[3] = 5003;
        ports[4] = 5004;

        Selector selector = Selector.open();
        for (int port : ports) {
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);

            ServerSocket serverSocket = serverSocketChannel.socket();
            InetSocketAddress address = new InetSocketAddress(port);
            // 绑定
            serverSocket.bind(address);

            // 将 selector 注册到 channel 上
            // 当有客户端向服务端发起连接的时候, 服务端就会获取这个连接, 与之建立真正的连接对象
            final SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT/* 对什么样的操作是感兴趣的 */);
            System.out.println("listening port: " + port + ". selection-key: " + selectionKey);
        }

        //noinspection InfiniteLoopStatement
        while (true) {
            final int keyNum = selector.select();
            System.out.println("select key num: " + keyNum);

            final Set<SelectionKey> selectionKeys = selector.selectedKeys(); // 为什么会返回一个集合? 因为会监听多个通道. See: selector
            System.out.println("selection keys: " + selectionKeys);

            final Iterator<SelectionKey> selectionKeyIterator = selectionKeys.iterator();
            while (selectionKeyIterator.hasNext()) {
                final SelectionKey selectionKey = selectionKeyIterator.next();

                if (selectionKey.isAcceptable()) {
                    // 接受真正的连接: 怎么接受真正的连接?
                    ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    socketChannel.configureBlocking(false);

                    socketChannel.register(selector, SelectionKey.OP_READ); // 真正的连接对象 (添加 SocketChannel 到 Selector 中)
                    selectionKeyIterator.remove(); // ! 不删除的话, 还会监听这个事件, 但实际上这个连接已经建立了 (↑)

                    System.out.println("accepted client's connection: " + socketChannel);
                } else if (selectionKey.isReadable()) {
                    // 数据读取
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();

                    int bytesRead = 0;
                    while (true) {
                        ByteBuffer byteBuffer = ByteBuffer.allocate(512);
                        byteBuffer.clear();

                        int read = socketChannel.read(byteBuffer);
                        if (read <= 0) {
                            break;
                        }

                        byteBuffer.flip();
                        socketChannel.write(byteBuffer);
                        bytesRead += read;
                    }
                    selectionKeyIterator.remove(); // !
                    System.out.println("read: " + bytesRead + ", from: " + socketChannel); // !
                }
            }
        }
    }
}
